package cn.schoolwow.quickflow.service.flowContext.getData;

import cn.schoolwow.quickflow.domain.FlowConfig;
import cn.schoolwow.quickflow.domain.FlowContext;
import cn.schoolwow.quickflow.domain.FlowExecutorConfig;
import cn.schoolwow.quickflow.dto.FlowContextDataRange;
import cn.schoolwow.quickflow.service.flowContext.putData.FlowContextPutDataOperation;
import cn.schoolwow.quickflow.util.QuickFlowUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;

import java.lang.reflect.Array;
import java.util.*;

public class FlowContextGetDataOperationImpl implements FlowContextGetDataOperation {
    private static final FlowContextDataRange[] ranges = new FlowContextDataRange[]{
            FlowContextDataRange.ThreadLocal,
            FlowContextDataRange.CurrentFlow,
            FlowContextDataRange.CurrentCompositeBusiness,
            FlowContextDataRange.Flow,
            FlowContextDataRange.ContextThreadLocal,
            FlowContextDataRange.Context
    };
    private FlowExecutorConfig flowExecutorConfig;
    private FlowContextPutDataOperation flowContextPutDataOperation;

    public FlowContextGetDataOperationImpl(FlowExecutorConfig flowExecutorConfig, FlowContextPutDataOperation flowContextPutDataOperation) {
        this.flowExecutorConfig = flowExecutorConfig;
        this.flowContextPutDataOperation = flowContextPutDataOperation;
    }

    @Override
    public FlowContext isDataNotExist(String key, String message) {
        if(!containKey(key)){
            throw new IllegalArgumentException(message);
        }
        return flowExecutorConfig.flowContext;
    }

    @Override
    public boolean containKey(String key) {
        Object value = getData(key);
        return null!=value;
    }

    @Override
    public boolean containInstanceKey(Class clazz) {
        Object value = getInstanceData(clazz);
        return null!=value;
    }

    @Override
    public Object checkData(String key) {
        Object value = getData(key);
        checkValueEmpty(key, value);
        return value;
    }

    @Override
    public <T> T checkData(String key, Class<T> clazz) {
        T value = getData(key, clazz);
        checkValueEmpty(key, value);
        return value;
    }

    @Override
    public <T> T checkData(String key, TypeReference<T> typeReference) {
        T value = getData(key, typeReference);
        checkValueEmpty(key, value);
        return value;
    }

    @Override
    public <T> T checkInstanceData(Class<T> clazz) {
        String key = clazz.getName();
        T value = getData(key, clazz);
        checkValueEmpty(key, value);
        return value;
    }

    @Override
    public Object getData(String key) {
        if(flowExecutorConfig.flowExecutorRootConfig.flowConfig.flowConfigSwitcherContext.checkRepeatKeyName){
            List<String> existRangeList = new ArrayList<>();
            Object returnValue = null;
            for(FlowContextDataRange flowContextDataRange:ranges){
                Object value = getDataFromRange(key, flowContextDataRange);
                if(null!=value){
                    existRangeList.add(flowContextDataRange.name());
                    returnValue = value;
                }
            }
            if(null==returnValue){
                return null;
            }
            if(existRangeList.size()>1){
                throw new IllegalArgumentException("变量["+key+"]在作用域"+existRangeList+"均存在!");
            }
        }else{
            for(FlowContextDataRange flowContextDataRange:ranges){
                Object value = getDataFromRange(key, flowContextDataRange);
                if(null!=value){
                    return value;
                }
            }
        }
        return null;
    }

    @Override
    public <T> T getData(String key, Class<T> clazz) {
        Object value = getData(key);
        return QuickFlowUtil.convertToClassType(value, clazz);
    }

    @Override
    public <T> T getData(String key, TypeReference<T> typeReference) {
        Object value = getData(key);
        if(value instanceof JSONObject){
            JSONObject object = (JSONObject) value;
            return object.toJavaObject(typeReference);
        }
        if(value instanceof JSONArray){
            JSONArray array = (JSONArray) value;
            return array.toJavaObject(typeReference);
        }
        return (T) value;
    }

    @Override
    public Object getData(String key, Object defaultValue) {
        Object value = getData(key);
        if(null==value){
            flowContextPutDataOperation.putTemporaryData(key, defaultValue);
            return defaultValue;
        }else{
            return value;
        }
    }

    @Override
    public <T> T getData(String key, Class<T> clazz, T defaultValue) {
        T value = getData(key, clazz);
        if(null==value){
            flowContextPutDataOperation.putTemporaryData(key, defaultValue);
            return defaultValue;
        }else{
            return value;
        }
    }

    @Override
    public <T> T getInstanceData(Class<T> clazz) {
        String key = clazz.getName();
        return getData(key, clazz);
    }

    @Override
    public <T> T getInstanceData(Class<T> clazz, T defaultValue) {
        String key = clazz.getName();
        return getData(key, clazz, defaultValue);
    }

    @Override
    public Map<String, Object> getContextDataMap() {
        Map<String,Object> filterDataMap = new HashMap<>();

        FlowConfig flowConfig = flowExecutorConfig.flowExecutorRootConfig.flowConfig;
        Map<String,Object> dataMap = flowConfig.flowConfigDataContext.dataMap;
        filterDataMap.putAll(dataMap);
        removeEntryByKeySet(filterDataMap, flowConfig.flowConfigDataContext.flowContextDataKeySet.temporaryDataKeySet);
        return filterDataMap;
    }

    @Override
    public Map<String, Object> getFlowConfigDataMap() {
        Map<String,Object> filterDataMap = new HashMap<>();

        Map<String,Object> dataMap = flowExecutorConfig.flowExecutorRootConfig.dataMap;
        addEntryByKeySet(filterDataMap, dataMap, flowExecutorConfig.flowExecutorRootConfig.flowContextDataKeySet.flowConfigDataKeySet);
        return filterDataMap;
    }

    @Override
    public Map<String, Object> getFlowExecuteDataMap() {
        Map<String,Object> filterDataMap = new HashMap<>();

        Map<String,Object> dataMap = flowExecutorConfig.flowExecutorRootConfig.dataMap;
        filterDataMap.putAll(dataMap);
        Map<String,Object> threadLocalDataMap = flowExecutorConfig.flowExecutorRootConfig.threadLocalDataMap.get();
        if(null!=threadLocalDataMap){
            filterDataMap.putAll(threadLocalDataMap);
        }

        removeEntryByKeySet(filterDataMap, flowExecutorConfig.flowExecutorRootConfig.flowConfig.flowConfigDataContext.dataMap.keySet());
        removeEntryByKeySet(filterDataMap, flowExecutorConfig.flowExecutorRootConfig.flowConfig.flowConfigDataContext.flowContextDataKeySet.temporaryDataKeySet);
        removeEntryByKeySet(filterDataMap, flowExecutorConfig.flowExecutorRootConfig.flowContextDataKeySet.requestDataKeySet);
        removeEntryByKeySet(filterDataMap, flowExecutorConfig.flowExecutorRootConfig.flowContextDataKeySet.temporaryDataKeySet);
        removeEntryByKeySet(filterDataMap, flowExecutorConfig.flowExecutorRootConfig.flowContextDataKeySet.returnDataKeySet);
        return filterDataMap;
    }

    @Override
    public Map<String, Object> getDataMap() {
        return flowExecutorConfig.flowExecutorRootConfig.dataMap;
    }

    @Override
    public Map<String, Object> getRequestDataMap() {
        Map<String,Object> filterDataMap = new HashMap<>();
        {
            Map<String,Object> dataMap = flowExecutorConfig.flowExecutorRootConfig.dataMap;
            for(String key:flowExecutorConfig.flowExecutorRootConfig.flowContextDataKeySet.requestDataKeySet){
                if(dataMap.containsKey(key)){
                    if(flowExecutorConfig.flowExecutorRootConfig.flowContextDataKeySet.temporaryDataKeySet.contains(key)){
                        continue;
                    }
                    filterDataMap.put(key, dataMap.get(key));
                }
            }
        }
        //全局线程本地数据
        {
            Map<String,Object> threadLocalMap = flowExecutorConfig.flowExecutorRootConfig.flowConfig.flowConfigDataContext.threadLocalDataMap.get();
            if(null!=threadLocalMap){
                Set<String> temporaryDataKeySet = flowExecutorConfig.flowExecutorRootConfig.flowConfig.flowConfigDataContext.flowContextDataKeySet.temporaryDataKeySet;
                for(String key:threadLocalMap.keySet()){
                    if(!temporaryDataKeySet.contains(key)){
                        filterDataMap.put(key, threadLocalMap.get(key));
                    }
                }
            }
        }
        return filterDataMap;
    }

    @Override
    public Map<String, Object> getThreadLocalDataMap() {
        Map threadLocalMap = flowExecutorConfig.flowExecutorRootConfig.threadLocalDataMap.get();
        return threadLocalMap;
    }

    @Override
    public Map<String, Object> getCurrentFlowDataMap() {
        Map<String,Object> currentFlowData = flowExecutorConfig.currentFlowDataMap;
        return currentFlowData;
    }

    @Override
    public Map<String, Object> getCurrentCompositeFlowDataMap() {
        return flowExecutorConfig.currentCompositeFlowDataMap;
    }

    @Override
    public FlowContextDataRange getFlowContextDataRangeByKey(String key) {
        //从当前线程上下文中获取
        {
            Map threadLocalMap = flowExecutorConfig.flowExecutorRootConfig.threadLocalDataMap.get();
            if(null!=threadLocalMap&&!threadLocalMap.isEmpty()&&threadLocalMap.containsKey(key)){
                return FlowContextDataRange.ThreadLocal;
            }
        }
        //从当前流程获取
        {
            Map<String,Object> currentFlowDataMap = flowExecutorConfig.currentFlowDataMap;
            if(!currentFlowDataMap.isEmpty()&&currentFlowDataMap.containsKey(key)){
                return FlowContextDataRange.CurrentFlow;
            }
        }
        //从当前复合流程获取
        {
            Map<String,Object> currentCompositeFlowDataMap = flowExecutorConfig.currentCompositeFlowDataMap;
            if(!currentCompositeFlowDataMap.isEmpty()&&currentCompositeFlowDataMap.containsKey(key)){
                return FlowContextDataRange.CurrentCompositeBusiness;
            }
        }
        //从流程上下文获取
        {
            if(!flowExecutorConfig.flowExecutorRootConfig.dataMap.isEmpty()&&flowExecutorConfig.flowExecutorRootConfig.dataMap.containsKey(key)){
                return FlowContextDataRange.Context;
            }
        }
        //从全局流程线程上下文获取
        {
            Map<String,Object> contextThreadLocalMap = flowExecutorConfig.flowExecutorRootConfig.flowConfig.flowConfigDataContext.threadLocalDataMap.get();
            if(null!=contextThreadLocalMap&&!contextThreadLocalMap.isEmpty()&&contextThreadLocalMap.containsKey(key)){
                return FlowContextDataRange.ContextThreadLocal;
            }
        }
        {
            //从全局流程上下文获取
            Map<String,Object> contextDataMap = flowExecutorConfig.flowExecutorRootConfig.flowConfig.flowConfigDataContext.dataMap;
            if(!contextDataMap.isEmpty()&&contextDataMap.containsKey(key)){
                return FlowContextDataRange.Context;
            }
        }
        return null;
    }

    @Override
    public FlowContextDataRange getFlowContextDataRangeByInstance(Class clazz) {
        return getFlowContextDataRangeByKey(clazz.getName());
    }

    @Override
    public void printFlowDataRange() {
        StringBuilder builder = new StringBuilder();

        //从当前线程上下文中获取
        {
            Map<String,Object> threadLocalMap = flowExecutorConfig.flowExecutorRootConfig.threadLocalDataMap.get();
            if(null!=threadLocalMap&&!threadLocalMap.isEmpty()){
                builder.append("当前线程上下文:\r\n");
                for(String key:threadLocalMap.keySet()){
                    builder.append(key+"\r\n");
                }
            }
        }
        //从当前流程获取
        {
            Map<String,Object> currentFlowDataMap = flowExecutorConfig.currentFlowDataMap;
            if(!currentFlowDataMap.isEmpty()){
                builder.append("当前流程:\r\n");
                for(String key:currentFlowDataMap.keySet()){
                    builder.append(key+"\r\n");
                }
            }
        }
        //从当前复合流程获取
        {
            Map<String,Object> currentCompositeFlowDataMap = flowExecutorConfig.currentCompositeFlowDataMap;
            if(!currentCompositeFlowDataMap.isEmpty()){
                builder.append("当前复合流程:\r\n");
                for(String key:currentCompositeFlowDataMap.keySet()){
                    builder.append(key+"\r\n");
                }
            }
        }
        //从流程上下文获取
        {
            Map<String,Object> dataMap = flowExecutorConfig.flowExecutorRootConfig.dataMap;
            if(!dataMap.isEmpty()){
                builder.append("流程上下文:\r\n");
                for(String key:dataMap.keySet()){
                    builder.append(key+"\r\n");
                }
            }
        }
        //从全局流程线程上下文获取
        {
            Map<String,Object> contextThreadLocalMap = flowExecutorConfig.flowExecutorRootConfig.flowConfig.flowConfigDataContext.threadLocalDataMap.get();
            if(null!=contextThreadLocalMap&&!contextThreadLocalMap.isEmpty()){
                builder.append("全局流程线程上下文:\r\n");
                for(String key:contextThreadLocalMap.keySet()){
                    builder.append(key+"\r\n");
                }
            }
        }
        {
            //从全局流程上下文获取
            Map<String,Object> dataMap = flowExecutorConfig.flowExecutorRootConfig.flowConfig.flowConfigDataContext.dataMap;
            if(!dataMap.isEmpty()){
                builder.append("全局流程上下文:\r\n");
                for(String key:dataMap.keySet()){
                    builder.append(key+"\r\n");
                }
            }
        }
        System.out.println(builder);
    }

    private void checkValueEmpty(String key, Object value){
        if(null==value){
            printFlowDataRange();
            throw new IllegalArgumentException("上下文数据中key为"+key+"的值不能为空!");
        }
        if(value.getClass().isArray()){
            if(Array.getLength(value)==0){
                printFlowDataRange();
                throw new IllegalArgumentException("上下文数据中key为"+key+"的数组不能为空!");
            }
        }
        if(value instanceof Collection){
            if(((Collection)value).isEmpty()){
                printFlowDataRange();
                throw new IllegalArgumentException("上下文数据中key为"+key+"的列表不能为空!");
            }
        }
    }

    private void addEntryByKeySet(Map<String,Object> filterDataMap, Map<String,Object> dataMap, Set<String> keySet){
        for(String key:keySet){
            filterDataMap.put(key, dataMap.get(key));
        }
    }

    private void removeEntryByKeySet(Map<String,Object> dataMap, Set<String> keySet){
        for(String key:keySet){
            dataMap.remove(key);
        }
    }

    private Object getDataFromRange(String key, FlowContextDataRange flowContextDataRange){
        switch (flowContextDataRange){
            case ThreadLocal:{
                //从当前线程上下文中获取
                Map threadLocalMap = flowExecutorConfig.flowExecutorRootConfig.threadLocalDataMap.get();
                if(null!=threadLocalMap&&threadLocalMap.containsKey(key)){
                    Object value = threadLocalMap.get(key);
                    return value;
                }
            }break;
            case CurrentFlow:{
                //从当前流程获取
                Map<String,Object> currentFlowDataMap = flowExecutorConfig.currentFlowDataMap;
                if(null!=currentFlowDataMap&&currentFlowDataMap.containsKey(key)){
                    return currentFlowDataMap.get(key);
                }
            }break;
            case CurrentCompositeBusiness:{
                //从当前复合流程获取
                Map<String,Object> currentCompositeFlowDataMap = flowExecutorConfig.currentCompositeFlowDataMap;
                if(null!=currentCompositeFlowDataMap&&currentCompositeFlowDataMap.containsKey(key)){
                    return currentCompositeFlowDataMap.get(key);
                }
            }break;
            case Flow:{
                //从流程上下文获取
                if(flowExecutorConfig.flowExecutorRootConfig.dataMap.containsKey(key)){
                    Object value = flowExecutorConfig.flowExecutorRootConfig.dataMap.get(key);
                    if(flowExecutorConfig.flowExecutorRootConfig.flowContextDataKeySet.returnDataKeySet.contains(key)){
                        flowContextPutDataOperation.removeData(key);
                        flowExecutorConfig.flowExecutorRootConfig.flowContextDataKeySet.returnDataKeySet.remove(key);
                    }
                    return value;
                }
            }break;
            case ContextThreadLocal:{
                //从全局流程线程上下文获取
                Map<String,Object> contextThreadLocalMap = flowExecutorConfig.flowExecutorRootConfig.flowConfig.flowConfigDataContext.threadLocalDataMap.get();
                if(null!=contextThreadLocalMap&&contextThreadLocalMap.containsKey(key)){
                    return contextThreadLocalMap.get(key);
                }
            }break;
            case Context:{
                //从全局流程上下文获取
                if(flowExecutorConfig.flowExecutorRootConfig.flowConfig.flowConfigDataContext.dataMap.containsKey(key)){
                    return flowExecutorConfig.flowExecutorRootConfig.flowConfig.flowConfigDataContext.dataMap.get(key);
                }
            }break;
        }
        return null;
    }
}
